package com.zhiwen.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhiwen.common.exception.BusinessException;
import com.zhiwen.domain.dto.CommentDTO;
import com.zhiwen.domain.entity.Answer;
import com.zhiwen.domain.entity.Comment;
import com.zhiwen.domain.entity.User;
import com.zhiwen.mapper.CommentMapper;
import com.zhiwen.service.AnswerService;
import com.zhiwen.service.CommentService;
import com.zhiwen.service.NotificationProducer;
import com.zhiwen.service.UserService;
import com.zhiwen.domain.vo.CommentCountVO;
import com.zhiwen.domain.vo.CommentVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 评论服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
@CacheConfig(cacheNames = "comment")
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {

    private final UserService userService;
    private final AnswerService answerService;
    private final RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private NotificationProducer notificationProducer;

    // 定义Redis缓存键前缀
    private static final String COMMENT_COUNT_KEY = "comment:count:";
    private static final String COMMENT_USER_COUNT_KEY = "comment:user:count:";
    private static final String COMMENT_ANSWER_KEY = "comment:answer:";
    private static final String COMMENT_LATEST_KEY = "comment:latest";
    private static final long CACHE_EXPIRE_TIME = 1800L; // 缓存过期时间，单位秒
    private static final int BATCH_SIZE = 100; // 批量操作大小

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(key = "'answerId:' + #commentDTO.answerId")
    public CommentVO createComment(CommentDTO commentDTO) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 检查回答是否存在
        Answer answer = answerService.getById(commentDTO.getAnswerId());
        if (answer == null) {
            throw new BusinessException("回答不存在");
        }

        // 创建评论
        Comment comment = new Comment();
        comment.setAnswerId(commentDTO.getAnswerId());
        comment.setUserId(userId);
        comment.setContent(commentDTO.getContent());
        comment.setCreateTime(LocalDateTime.now());
        comment.setUpdateTime(LocalDateTime.now());
        comment.setDeleted(0);

        // 保存评论
        boolean saved = this.save(comment);
        if (!saved) {
            throw new BusinessException("保存评论失败");
        }

        // 更新回答的评论数量
        answer.setCommentCount(answer.getCommentCount() + 1);
        answerService.updateById(answer);

        // 清除缓存
        clearCommentCountCache(userId);

        // 清除最新评论缓存
        clearLatestCommentsCache();

        // 异步发送通知
        sendCommentNotificationAsync(answer, comment, userId);

        // 返回评论VO
        return convertToVO(comment, userService.getById(userId));
    }

    /**
     * 异步发送评论通知
     */
    @Async
    protected void sendCommentNotificationAsync(Answer answer, Comment comment, Long userId) {
        try {
            // 如果评论不是自己的回答，发送评论通知
            if (!answer.getUserId().equals(userId)) {
                notificationProducer.sendAnswerCommentedNotification(
                        answer.getId(),
                        comment.getId(),
                        answer.getUserId(), // 回答作者ID（接收者）
                        userId // 评论作者ID（发送者）
                );
            }
        } catch (Exception e) {
            log.error("发送评论通知失败", e);
            // 通知失败不影响主流程
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(key = "'answerId:' + #result.answerId")
    public CommentVO updateComment(CommentDTO commentDTO) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 获取评论信息
        Comment comment = getById(commentDTO.getId());
        if (comment == null) {
            throw new BusinessException("评论不存在");
        }

        // 检查权限（只有评论作者可以修改评论）
        if (!userId.equals(comment.getUserId())) {
            throw new BusinessException("无权限修改该评论");
        }

        // 检查回答是否存在
        Answer answer = answerService.getById(comment.getAnswerId());
        if (answer == null) {
            throw new BusinessException("评论关联的回答不存在");
        }

        // 更新评论
        comment.setContent(commentDTO.getContent());
        comment.setUpdateTime(LocalDateTime.now());

        // 保存更新
        boolean updated = updateById(comment);
        if (!updated) {
            throw new BusinessException("更新评论失败");
        }

        // 清除最新评论缓存
        clearLatestCommentsCache();

        // 返回评论VO
        return convertToVO(comment, userService.getById(userId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(key = "'answerId:' + #result.answerId")
    public CommentVO getCommentById(Long id) {
        Comment comment = getById(id);
        if (comment == null || comment.getDeleted() == 1) {
            throw new BusinessException("评论不存在或已删除");
        }

        return convertToVO(comment, userService.getById(comment.getUserId()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(key = "'answerId:' + #result")
    public Long deleteComment(Long id) {
        // 获取当前登录用户
        Long userId = StpUtil.getLoginIdAsLong();

        // 查询评论
        Comment comment = getById(id);
        if (comment == null) {
            throw new BusinessException("评论不存在");
        }

        // 获取回答ID用于返回和缓存清除
        Long answerId = comment.getAnswerId();

        // 检查权限（只有评论作者或管理员可以删除）
        User user = userService.getById(userId);
        if (!userId.equals(comment.getUserId()) && user.getRole() != 3) {
            throw new BusinessException("无权限删除该评论");
        }

        // 删除评论（逻辑删除）
        removeById(id);

        // 更新回答的评论数
        Answer answer = answerService.getById(answerId);
        if (answer != null && answer.getCommentCount() > 0) {
            answer.setCommentCount(answer.getCommentCount() - 1);
            answerService.updateById(answer);
        }

        // 清除缓存
        clearCommentCountCache(userId);

        // 清除最新评论缓存
        clearLatestCommentsCache();

        return answerId;
    }

    /**
     * 清除评论计数相关缓存
     */
    private void clearCommentCountCache(Long userId) {
        try {
            String totalCountKey = COMMENT_COUNT_KEY + "total";
            String userCountKey = COMMENT_USER_COUNT_KEY + userId;
            redisTemplate.delete(totalCountKey);
            redisTemplate.delete(userCountKey);
        } catch (Exception e) {
            log.error("清除评论计数缓存失败", e);
        }
    }

    /**
     * 清除最新评论缓存
     */
    private void clearLatestCommentsCache() {
        try {
            redisTemplate.delete(COMMENT_LATEST_KEY);
        } catch (Exception e) {
            log.error("清除最新评论缓存失败", e);
        }
    }

    @Override
    @Cacheable(key = "'answerId:' + #answerId", unless = "#result.records.isEmpty()")
    public Page<CommentVO> getCommentPage(Long answerId, Page<Comment> page) {
        // 构建查询条件
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Comment::getAnswerId, answerId);
        wrapper.eq(Comment::getDeleted, 0);
        wrapper.orderByDesc(Comment::getCreateTime);

        // 分页查询
        Page<Comment> commentPage = page(page, wrapper);

        // 如果结果为空，直接返回空页
        if (CollectionUtils.isEmpty(commentPage.getRecords())) {
            return new Page<CommentVO>().setRecords(new ArrayList<>())
                    .setCurrent(page.getCurrent())
                    .setSize(page.getSize())
                    .setTotal(0);
        }

        // 批量获取用户信息，减少数据库查询次数
        return convertToVOPage(commentPage);
    }

    @Override
    public Page<CommentVO> getUserComments(Long userId, Page<Comment> page) {
        // 构建查询条件
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Comment::getUserId, userId);
        wrapper.eq(Comment::getDeleted, 0);
        wrapper.orderByDesc(Comment::getCreateTime);

        // 分页查询
        Page<Comment> commentPage = page(page, wrapper);

        // 如果结果为空，直接返回空页
        if (CollectionUtils.isEmpty(commentPage.getRecords())) {
            return new Page<CommentVO>().setRecords(new ArrayList<>())
                    .setCurrent(page.getCurrent())
                    .setSize(page.getSize())
                    .setTotal(0);
        }

        // 只获取当前用户信息，避免多次查询
        User user = userService.getById(userId);

        // 转换结果
        List<CommentVO> commentVOList = commentPage.getRecords().stream()
                .map(comment -> convertToVO(comment, user))
                .collect(Collectors.toList());

        // 构建返回结果
        Page<CommentVO> commentVOPage = new Page<>();
        BeanUtils.copyProperties(commentPage, commentVOPage, "records");
        commentVOPage.setRecords(commentVOList);

        return commentVOPage;
    }

    @Override
    @Cacheable(key = "'latest:' + #limit", unless = "#result.isEmpty()")
    public List<CommentVO> getLatestComments(int limit) {
        // 限制数量，防止请求过大
        int actualLimit = Math.min(limit, 50);

        // 构建查询条件
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Comment::getDeleted, 0);
        wrapper.orderByDesc(Comment::getCreateTime);
        wrapper.last("LIMIT " + actualLimit);

        // 查询最新评论
        List<Comment> comments = list(wrapper);
        if (CollectionUtils.isEmpty(comments)) {
            return new ArrayList<>();
        }

        // 批量查询用户信息
        List<Long> userIds = comments.stream()
                .map(Comment::getUserId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());

        Map<Long, User> userMap = userService.listByIds(userIds).stream()
                .collect(Collectors.toMap(User::getId, user -> user, (u1, u2) -> u1));

        // 转换为VO
        return comments.stream()
                .map(comment -> {
                    User user = userMap.get(comment.getUserId());
                    return convertToVO(comment, user);
                })
                .collect(Collectors.toList());
    }

    @Override
    public CommentCountVO getCommentCount(Long userId) {
        // 尝试从缓存获取
        String totalCountKey = COMMENT_COUNT_KEY + "total";
        String userCountKey = COMMENT_USER_COUNT_KEY + userId;

        // 获取系统总评论数
        Long totalCount = getCommentCountFromCache(totalCountKey, () -> {
            LambdaQueryWrapper<Comment> totalWrapper = new LambdaQueryWrapper<>();
            totalWrapper.eq(Comment::getDeleted, 0);
            return count(totalWrapper);
        });

        // 获取用户评论数
        Long userCount = getCommentCountFromCache(userCountKey, () -> {
            LambdaQueryWrapper<Comment> userWrapper = new LambdaQueryWrapper<>();
            userWrapper.eq(Comment::getUserId, userId);
            userWrapper.eq(Comment::getDeleted, 0);
            return count(userWrapper);
        });

        // 返回统计结果
        return CommentCountVO.builder()
                .totalCount(totalCount)
                .userCount(userCount)
                .build();
    }

    /**
     * 从缓存获取评论数量，如果缓存不存在则执行查询并缓存结果
     */
    private Long getCommentCountFromCache(String key, java.util.function.Supplier<Long> countSupplier) {
        try {
            Object cachedCount = redisTemplate.opsForValue().get(key);
            if (cachedCount != null) {
                return Long.valueOf(cachedCount.toString());
            }

            Long count = countSupplier.get();
            redisTemplate.opsForValue().set(key, count, CACHE_EXPIRE_TIME, TimeUnit.SECONDS);
            return count;
        } catch (Exception e) {
            log.error("从缓存获取评论数量失败，回退到直接查询", e);
            // 缓存失败时回退到直接查询
            return countSupplier.get();
        }
    }

    /**
     * 获取所有评论列表（管理员使用）
     */
    @Override
    public Page<CommentVO> getAllComments(Long answerId, Page<Comment> page) {
        // 构建查询条件
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();

        // 如果指定了回答ID，则按回答ID筛选
        if (answerId != null) {
            wrapper.eq(Comment::getAnswerId, answerId);
        }

        // 未删除条件
        wrapper.eq(Comment::getDeleted, 0);

        // 排序：创建时间倒序
        wrapper.orderByDesc(Comment::getCreateTime);

        // 分页查询
        Page<Comment> commentPage = page(page, wrapper);

        // 批量转换数据
        return convertToVOPage(commentPage);
    }

    /**
     * 将评论实体转换为VO
     */
    private CommentVO convertToVO(Comment comment, User user) {
        CommentVO commentVO = new CommentVO();
        BeanUtils.copyProperties(comment, commentVO);

        // 设置用户信息
        if (user != null) {
            commentVO.setUsername(user.getUsername());
            commentVO.setNickname(user.getNickname());
            commentVO.setAvatar(user.getAvatar());
        }

        return commentVO;
    }

    /**
     * 将评论实体转换为VO
     */
    private CommentVO convertToVO(Comment comment) {
        return convertToVO(comment, userService.getById(comment.getUserId()));
    }

    /**
     * 将评论分页结果批量转换为VO分页结果
     */
    private Page<CommentVO> convertToVOPage(Page<Comment> commentPage) {
        if (CollectionUtils.isEmpty(commentPage.getRecords())) {
            Page<CommentVO> emptyPage = new Page<>();
            BeanUtils.copyProperties(commentPage, emptyPage, "records");
            emptyPage.setRecords(new ArrayList<>());
            return emptyPage;
        }

        // 提取所有用户ID
        List<Long> userIds = commentPage.getRecords().stream()
                .map(Comment::getUserId)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());

        // 批量查询用户信息
        List<User> users = userService.listByIds(userIds);
        Map<Long, User> userMap = users.stream()
                .collect(Collectors.toMap(User::getId, user -> user, (u1, u2) -> u1));

        // 转换评论结果
        List<CommentVO> commentVOList = commentPage.getRecords().stream()
                .map(comment -> {
                    User user = userMap.get(comment.getUserId());
                    return convertToVO(comment, user);
                })
                .collect(Collectors.toList());

        // 构建返回结果
        Page<CommentVO> commentVOPage = new Page<>();
        BeanUtils.copyProperties(commentPage, commentVOPage, "records");
        commentVOPage.setRecords(commentVOList);

        return commentVOPage;
    }

    /**
     * 批量删除指定回答下的所有评论
     *
     * @param answerId 回答ID
     * @return 删除的评论数量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int batchDeleteByAnswerId(Long answerId) {
        if (answerId == null) {
            return 0;
        }

        // 查询该回答下所有未删除的评论
        LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Comment::getAnswerId, answerId);
        wrapper.eq(Comment::getDeleted, 0);

        // 执行批量逻辑删除
        List<Comment> comments = list(wrapper);
        if (CollectionUtils.isEmpty(comments)) {
            return 0;
        }

        // 分批处理，避免一次性操作数据过多
        int total = 0;
        List<List<Comment>> batches = getBatches(comments, BATCH_SIZE);
        for (List<Comment> batch : batches) {
            List<Long> ids = batch.stream().map(Comment::getId).collect(Collectors.toList());
            boolean success = removeByIds(ids);
            if (success) {
                total += batch.size();
            }
        }

        // 更新回答的评论数为0
        if (total > 0) {
            Answer answer = answerService.getById(answerId);
            if (answer != null) {
                answer.setCommentCount(0);
                answerService.updateById(answer);

                // 清除缓存
                clearAnswerCommentCache(answerId);

                // 清除最新评论缓存
                clearLatestCommentsCache();
            }
        }

        return total;
    }

    /**
     * 清除回答评论相关缓存
     */
    private void clearAnswerCommentCache(Long answerId) {
        try {
            String key = COMMENT_ANSWER_KEY + answerId;
            redisTemplate.delete(key);
        } catch (Exception e) {
            log.error("清除回答评论缓存失败", e);
        }
    }

    /**
     * 将集合分成指定大小的批次
     */
    private <T> List<List<T>> getBatches(List<T> list, int batchSize) {
        List<List<T>> batches = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            int endIndex = Math.min(i + batchSize, list.size());
            batches.add(list.subList(i, endIndex));
        }
        return batches;
    }
}
